package emby.lang;

import java.io.Serializable;

import emby.ASTNode;
import emby.err.RuntimeError;
import emby.experimental.EmbyParallelFunction;
import emby.expr.Variable;
import emby.overload.OverloadCallable;
import emby.overload.OverloadDot;
import emby.overload.OverloadType;

/**
 * The base class for all emby functions.
 * @author Justin
 */
public class EmbyFunction extends EmbyObject implements Serializable, OverloadCallable, OverloadDot, OverloadType{
	
	public static EmbyParallelFunction toParallel(EmbyFunction f){
		return new EmbyParallelFunction(f.name,f.definedScope, f.body, f.params);
	}
	
	private static final long serialVersionUID = 5146977282908852219L;
	public final String name;
	private Variable[] params;
	private Block body;
	private Scope definedScope;

	public EmbyFunction(String _name, Scope _definedScope, Block _b, Variable... _params){
		name = _name;
		setDefinedScope(_definedScope);
		body = _b;
		params = _params;
	}


	/**
	 *
	 * @param lineNumber The line that the function was called at. For error reporting purposes.
	 * @param s The scope that the function was CALLED IN. Not defined in.
	 * @param args Arguments of the function call
	 * @return result of the call
	 */
	public Object execute(int lineNumber, Scope s,Object... args){
		return this._execute(lineNumber, s, args);
	}

	public Object _execute(int lineNumber, Scope s,Object... args){
		Scope funcScope = new Scope( getDefinedScope());
		if( args.length!= params.length)
			throw new RuntimeError(lineNumber,"Function "+name+" requires "+params.length+" arg(s). Found "+args.length);
		for( int i = 0; i< params.length; i++){
			funcScope.setVar( params[i].getName(), args[i]);
		}
		return body.eval(funcScope);
	}
	
	public int numArgs(){
		return params.length;
	}

	/**
	 * @deprecated please DONT use!
	 * @param op
	 */
	@Deprecated
	public void appendOperation(ASTNode op){
		body.add(op);
	}

	/**
	 * @deprecated please DONT use! This method was created as a small hack for quickly implementing OOP.
	 * @param op
	 */
	@Deprecated
	public void replaceLast(ASTNode op){
		body.operations[body.operations.length-1] = op;
	}

	public String toString(){
		return "<function \""+name+"\">";
	}

	@Override
	public Object get_dot(int line, String o) {
		if(o.equals("name")){
			return name;
		}
		else if(o.equals("__definedscope__")){
			return getDefinedScope();
		}
		else{
			throw new RuntimeError(line, "EmbyFunction has no attribute: "+o);
		}
	}

	@Override
	public void set_dot(int line, String o, Object new_value) {
		throw new RuntimeError(line, "Yay!"+o+", "+new_value);
	}

	/**
	 * @return the definedScope
	 */
	public Scope getDefinedScope() {
		return definedScope;
	}

	/**
	 * @param definedScope the definedScope to set
	 */
	public void setDefinedScope(Scope definedScope) {
		this.definedScope = definedScope;
	}

	@Override
	public Object operatorType(int line, Scope s) {
		return "function";
	}

}